package qc.module.qms.api.controller;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Function;
import net.sf.jsqlparser.parser.CCJSqlParserManager;
import net.sf.jsqlparser.parser.SimpleNode;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.*;
import qc.common.core.constants.QCAuthConstant;
import qc.common.core.enums.ColumnTypeEnum;
import qc.common.core.enums.DataTypeEnum;
import qc.common.core.enums.TimeRangeLengthEnum;
import qc.common.core.exception.QCPromptException;
import qc.common.core.json.JsonParserUtil;
import qc.common.core.pair.KeyValuePairDto;
import qc.common.core.table.QCTable;
import qc.common.core.table.QCTableCell;
import qc.common.core.table.QCTableRow;
import qc.common.core.unify.QCUnifyReturnValue;
import qc.common.core.utils.DateUtil;
import qc.common.core.utils.NumberUtil;
import qc.common.core.utils.StringSplitUtil;
import qc.common.core.utils.TimeSpanUtil;
import qc.module.platform.dto.dbTable.DBTableFullDto;
import qc.module.platform.dto.dbTableColumn.DBTableColumnDto;
import qc.module.platform.dto.dbTableData.DBTableDataOperateDto;
import qc.module.platform.dto.dbTableSql.DBTableSqlDto;
import qc.module.platform.dto.dbTableSql.DBTableSqlSimpleDto;
import qc.module.platform.dto.uac.UacUserInfoDto;
import qc.module.qms.api.RestTemplatePlatform;
import qc.module.qms.dto.QmsDataDto;
import qc.module.qms.dto.exchange.category.ExchangeCategoryDto;
import qc.module.qms.dto.exchange.channel.ExchangeChannelDto;
import qc.module.qms.dto.exchange.config.ExchangeCategoryItemDto;
import qc.module.qms.dto.exchange.config.ExchangeChannelItemDto;
import qc.module.qms.dto.exchange.config.ExchangeConfigQueryConditionDto;
import qc.module.qms.dto.exchange.config.ExchangeConfigQueryResultDto;
import qc.module.qms.dto.exchange.data.*;
import qc.module.qms.dto.exchange.log.DataExchangeLogDto;
import qc.module.qms.dto.exchange.log.DataExchangeLogQueryConditionDto;
import qc.module.qms.dto.station.StationDto;
import qc.module.qms.entity.ExchangeCategoryStation;
import qc.module.qms.service.QmsStationService;
import qc.module.qms.service.exchange.*;

import javax.servlet.http.HttpServletRequest;
import java.io.StringReader;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 数据交换数据管理Controller，用于查询和管理交换数据
 *
 * @author QuCheng Tech
 * @since 2024/1/23
 */
@RestController
@RequestMapping("/exchdata")
public class ExchangeDataController {
    private ExchangeChannelService channelService;
    private ExchangeCategoryService categoryService;
    private QmsStationService stationService;
    private ExchangeCategoryStationService categoryStationService;
    private ExchangeDataService exchangeDataService;
    private ExchangeLogService exchangeLogService;

    @Autowired
    public void setExchangeChannelService(ExchangeChannelService channelService) {
        this.channelService = channelService;
    }

    @Autowired
    public void setExchangeCategoryService(ExchangeCategoryService categoryService) {
        this.categoryService = categoryService;
    }

    @Autowired
    public void setExchangeStationService(QmsStationService stationService) {
        this.stationService = stationService;
    }

    @Autowired
    public void setExchangeCategoryStationService(ExchangeCategoryStationService categoryStationService) {
        this.categoryStationService = categoryStationService;
    }

    @Autowired
    public void setExchangeDataService(ExchangeDataService exchangeDataService) {
        this.exchangeDataService = exchangeDataService;
    }

    @Autowired
    public void setExchangeLogService(ExchangeLogService exchangeLogService) {
        this.exchangeLogService = exchangeLogService;
    }

    @Autowired
    HttpServletRequest request;

    @Autowired
    private RestTemplatePlatform restTemplatePlatform;

    /***
     * 根据指定输入得到查询交换数据条件选择输入数据
     *
     * @param condition 查询输入条件
     * @return qc.module.qms.dto.exchange.config.ExchangeConfigQueryResultDto
     * @author QuCheng Tech
     * @since 2024/1/24
     */
    @RequestMapping(value = "/condition", method = {RequestMethod.POST, RequestMethod.GET})
    public ExchangeConfigQueryResultDto getQueryConditionData(@RequestBody ExchangeConfigQueryConditionDto condition) throws QCPromptException {
        String channelIdString = null;
        String categoryIdString = null;
        String stationCodeString = null;

        if (condition != null) {
            channelIdString = condition.getChannel();
            categoryIdString = condition.getCategory();
            stationCodeString = condition.getStcd();
        }

        return getExchangeConfigQueryResult(channelIdString, categoryIdString, stationCodeString);
    }

    /***
     * 根据指定输入得到查询交换数据条件选择输入数据，使用url中进行传值
     *
     * @param category 数据分类ID，可以为空，可以为多个，多个使用逗号分隔
     * @param channel 通道ID，可以为空，可以为多个，多个使用逗号分隔
     * @param stcd 站点编码，可以为空，可以为多个，多个使用逗号分隔
     * @return qc.module.qms.dto.exchange.config.ExchangeConfigQueryResultDto
     * @author QuCheng Tech
     * @since 2024/1/24
     */
    @RequestMapping(value = "/condition1", method = {RequestMethod.POST, RequestMethod.GET})
    public ExchangeConfigQueryResultDto getQueryConditionDataByParam(@RequestParam(value = "channel", required = false) String channel,
                                                                     @RequestParam(value = "category", required = false) String category,
                                                                     @RequestParam(value = "stcd", required = false) String stcd) throws QCPromptException {

        return getExchangeConfigQueryResult(channel, category, stcd);
    }

    /***
     * 根据指定输入得到查询交换数据条件选择输入数据，按照通道-->数据分类-->站点的顺序和关系进行选择；
     * 指定站点编码时，自动根据站点编码获取数据分类信息，并根据分类信息获取通道信息；
     * 指定数据分类ID时，自动根据分类ID获取通道信息；
     * 指定通道ID时，自动根据通道ID获取通道下所有的数据分类和站点信息；
     * 均为指定时，获取交换通道中所有的通道，根据通道逐个获取通道下所有的数据分类和站点信息；
     *
     * @param channelIdString 通道ID，可以为空，可以为多个，多个使用逗号分隔
     * @param categoryIdString 数据分类ID，可以为空，可以为多个，多个使用逗号分隔
     * @param stationCodeString 站点编码，可以为空，可以为多个，多个使用逗号分隔
     * @return qc.module.qms.dto.exchange.config.ExchangeConfigQueryResultDto
     * @author QuCheng Tech
     * @since 2024/1/24
     */
    ExchangeConfigQueryResultDto getExchangeConfigQueryResult(String channelIdString, String categoryIdString, String stationCodeString) throws QCPromptException {
        //根据token得到用户所在的部门集合，用于后面根据用户部门进行过滤站点权限控制
        HttpHeaders headers = new HttpHeaders();
        headers.add(QCAuthConstant.TOKEN_HEADER_KEY, request.getHeader(QCAuthConstant.TOKEN_HEADER_KEY));
        headers.add(QCAuthConstant.PROJECT_CODE_HEADER_KEY, request.getHeader(QCAuthConstant.PROJECT_CODE_HEADER_KEY));
        //此处不需要判断token是否为空、是否有效，在调用platform中已经进处理，直接传入
        List<Integer> userDeptIds = restTemplatePlatform.getUserRelatedDeptIds(headers);
        if (userDeptIds == null || userDeptIds.size() < 0x1)
            throw new QCPromptException("根据用户获取到的关联部门信息为空，用户未关联部门无法获取有访问权限的站点");

        //传入的数据分类ID、通道ID和站点编码均可以为空或多个，先对传入的字符串进行分隔处理
        String[] channelIds = StringSplitUtil.Split(channelIdString, true);
        String[] categoryIds = StringSplitUtil.Split(categoryIdString, true);
        String[] stcds = StringSplitUtil.Split(stationCodeString, true);
        //前端输入的值不管是否正确均以前端输入的为准，否则会出现无访问权限的数据返回到前端中
        //过滤得到有权限的站点编码集合，同时判断站点与用户部门关联关系，如果请求param中指定的stcd不为空同时判断
        List<String> allValidStcds = null;
        if (stcds != null && stcds.length > 0x0)
            allValidStcds = stationService.getValidStcdsByUserDeptIds(userDeptIds, Arrays.asList(stcds));
        else
            allValidStcds = stationService.getValidStcdsByUserDeptIds(userDeptIds, null);
        if (allValidStcds == null || allValidStcds.size() < 0x1)
            throw new QCPromptException("根据用户获取到的有访问权限的站点为空");

        //根据有权限的站点编码获取站点信息
        List<StationDto> allValidStations = null;
        if (allValidStcds != null && allValidStcds.size() > 0x0)
            allValidStations = stationService.getByFilterStationCodes(allValidStcds.stream().toArray(String[]::new));

        //根据不同的输入条件，先对通道、数据分类和站点信息进行查询，后面根据结果进行统一处理
        List<ExchangeChannelDto> resultChannels = new ArrayList<>();
        List<ExchangeCategoryDto> resultCategories = new ArrayList<>();
        List<StationDto> resultStations = new ArrayList<>();
        List<ExchangeCategoryStation> resultCategoryStations = new ArrayList<>();
        //判断处理从最小范围开始，按照站点-->数据分类-->通道的顺序进行处理
        if (stcds != null && stcds.length > 0x0) {
            //先查询站点信息，再根据站点编码查询所属的数据分类信息，再根据数据分类ID查询所属的通道信息
            if (allValidStations != null && allValidStations.size() > 0x0) {
                //查询的站点信息不为空，根据站点编码查询分类
                resultCategories = categoryService.getByStationCodes(allValidStations.stream().map(p -> p.getStcd()).toArray(String[]::new));

                if (resultCategories != null && resultCategories.size() > 0x0) {
                    //查询的分类信息不为空，根据分类信息查询通道
                    resultChannels = channelService.getByCategoryIds(resultCategories.stream().map(p -> p.getId()).toArray(Integer[]::new));
                    //根据数据分类查询关联站点信息
                    resultCategoryStations = categoryStationService.getCategoryStations(resultCategories.stream().map(p -> p.getId()).toArray(Integer[]::new));
                }
            }
        } else if (categoryIds != null && categoryIds.length > 0x0) {
            //先转换数据分类ID，查询数据分类信息，再根据数据分类ID查询所属的通道信息，查询分类中的站点信息
            //数据分类ID、通道ID必须能转换为int数值
            List<Integer> intCategoryIds = new ArrayList<>();
            for (String str : categoryIds) {
                try {
                    int intValue = Integer.parseInt(str);
                    intCategoryIds.add(intValue);
                } catch (NumberFormatException ex) {
                    //转换错误不进行处理
                }
            }
            resultCategories = categoryService.getByFilterIds(intCategoryIds.stream().toArray(Integer[]::new));
            if (resultCategories != null && resultCategories.size() > 0x0) {
                //查询的分类信息不为空，根据分类信息查询通道
                resultChannels = channelService.getByCategoryIds(resultCategories.stream().map(p -> p.getId()).toArray(Integer[]::new));

                //根据数据分类查询关联站点信息
                resultCategoryStations = categoryStationService.getCategoryStations(resultCategories.stream().map(p -> p.getId()).toArray(Integer[]::new));
            }
        } else if (channelIds != null && channelIds.length > 0x0) {
            //先转换通道ID，查询通道信息，查询通道中的数据分类信息，查询数据分类中的站点信息
            List<Integer> intChannelIds = new ArrayList<>();
            for (String str : channelIds) {
                try {
                    int intValue = Integer.parseInt(str);
                    intChannelIds.add(intValue);
                } catch (NumberFormatException ex) {
                    //转换错误不进行处理
                }
            }
            resultChannels = channelService.getByFilterIds(intChannelIds.stream().toArray(Integer[]::new));
            if (resultChannels != null && resultChannels.size() > 0x0) {
                //查询的通道信息不为空，根据通道信息查询分类
                resultCategories = categoryService.getByChannelIds(resultChannels.stream().map(p -> p.getId()).toArray(Integer[]::new));
                if (resultCategories != null && resultCategories.size() > 0x0) {
                    //根据数据分类查询关联站点信息
                    resultCategoryStations = categoryStationService.getCategoryStations(resultCategories.stream().map(p -> p.getId()).toArray(Integer[]::new));
                }
            }
        } else {
            //均没有传入，返回所有通道及所有分类和站点
            resultChannels = channelService.getAll();
            resultCategories = categoryService.getAll();
            resultStations = stationService.getAll();
            resultCategoryStations = categoryStationService.getAllCategoryStation();
        }

        //for (StationDto stationDto : allValidStations) {
        //    System.out.println("foreach allValidStations  stationDto.stcd：" + stationDto.getStcd() + " stationDto.stnm=" + stationDto.getStnm());
        //}

        //得到的分类关联站点信息不为空时，根据站点编码集合信息查询站点信息
        if (resultCategoryStations != null && resultCategoryStations.size() > 0x0) {
            //for (ExchangeCategoryStation categoryStation : resultCategoryStations) {
            //    System.out.println("foreach resultCategoryStations  categoryStation.getCid：" + categoryStation.getCid() + " categoryStation.getStcd=" + categoryStation.getStcd());
            //}
            //获取数据分类中的站点集合与有权限站点集合的交集，如果在数据分类的站点中但没有权限也不显示该站点
            List<String> categoryStcds = resultCategoryStations.stream().map(p -> p.getStcd()).collect(Collectors.toList());
            if (allValidStations != null && allValidStations.size() > 0x0) {
                for (StationDto stationDto : allValidStations) {
                    if (categoryStcds.contains(stationDto.getStcd()))
                        resultStations.add(stationDto);
                }
            }
        }

        //判断根据不同输入得到的信息有效，需要从通道信息开始
        if (resultChannels != null && resultChannels.size() > 0x0) {
            ExchangeConfigQueryResultDto result = new ExchangeConfigQueryResultDto();
            List<ExchangeChannelItemDto> channelItems = new ArrayList<>();
            List<DBTableSqlSimpleDto> resultSqls = new ArrayList<>();

            //遍历交换通道
            for (ExchangeChannelDto channel : resultChannels) {
                ExchangeChannelItemDto channelItemDto = new ExchangeChannelItemDto();
                channelItemDto.setChannel(channel);
                //通道中的分类信息
                if (resultCategories != null && resultCategories.size() > 0x0) {
                    //根据当前通道ID筛选分类
                    List<ExchangeCategoryDto> channelCategories = resultCategories.stream().filter(p -> p.getCid() == channel.getId()).collect(Collectors.toList());
                    if (channelCategories != null && channelCategories.size() > 0x0) {
                        List<ExchangeCategoryItemDto> channelCategoryItems = new ArrayList<>();
                        //遍历当前通道中的分类
                        for (ExchangeCategoryDto channelCategory : channelCategories) {
                            ExchangeCategoryItemDto categoryItem = new ExchangeCategoryItemDto();
                            categoryItem.setCategory(channelCategory);
                            //根据数据分类对应的表名获取sql信息
                            System.out.println("channelCategory.getId()=" + channelCategory.getId() + " channelCategory.getTablecode()=" + channelCategory.getTablecode());
                            List<DBTableSqlSimpleDto> categorySqls = restTemplatePlatform.getDbTableSqlSimples(channelCategory.getTablecode());
                            if (categorySqls != null && categorySqls.size() > 0x0) {
                                categoryItem.setSqls(categorySqls);
                                //处理返回结果中的sql取并集，如果已经存在相同的sql语句代码不进行添加
                                List<String> existSqls = resultSqls.stream().map(p -> p.getCode()).collect(Collectors.toList());
                                for (DBTableSqlSimpleDto sql : categorySqls) {
                                    if (existSqls.size() < 0x1 || !existSqls.contains(sql.getCode())) {
                                        resultSqls.add(sql);
                                    }
                                }
                            }
                            //获取数据分类中的站点集合
                            if (resultStations != null && resultStations.size() > 0x0) {
                                //for (StationDto stationDto : resultStations) {
                                //    System.out.println("根据分类ID获取站点信息，for resultStations  stationDto.stcd：" + stationDto.getStcd() + " stationDto.stnm=" + stationDto.getStnm());
                                //}
                                //根据当前的分类ID筛选站点，由于使用分类与站点关联表，需要先根据分类ID筛选处站点编码集合
                                if (resultCategoryStations != null && resultCategoryStations.size() > 0x0) {
                                    //根据分类ID筛选站点编码
                                    List<String> categoryStcds = resultCategoryStations.stream().filter(p -> p.getCid() == channelCategory.getId()).map(p -> p.getStcd()).collect(Collectors.toList());
                                    if (categoryStcds != null && categoryStcds.size() > 0x0) {
                                        //根据站点编码筛选站点
                                        List<StationDto> filterCategoryStations = resultStations.stream().filter(p -> categoryStcds.contains(p.getStcd())).collect(Collectors.toList());
                                        if (filterCategoryStations != null && filterCategoryStations.size() > 0x0) {
                                            System.out.println("根据分类ID获取站点信息，分类ID：" + channelCategory.getId() + "得到的站点数量为：" + categoryStcds.size() + " stations数量为：" + resultStations.size() + " filterCategoryStations数量为： " + filterCategoryStations.size());
                                            categoryItem.setStations(filterCategoryStations);
                                        }
                                    }
                                }
                            }
                            channelCategoryItems.add(categoryItem);
                        }
                        channelItemDto.setCategories(channelCategoryItems);
                    }
                }

                channelItems.add(channelItemDto);
            }

            result.setChannels(channelItems);
            result.setSqls(resultSqls);
            return result;
        }

        return null;
    }

    /***
     * 查询交换数据--返回QcTable结果类型
     *
     * @param condition 查询条件
     * @return qc.module.qms.dto.exchangeData.ExchangeDataQueryResultDto
     * @author QuCheng Tech
     * @since 2024/1/28
     */
    @RequestMapping(value = "/query", method = {RequestMethod.POST, RequestMethod.GET})
    public ExchangeDataQueryQcTableResultDto queryQcTableResult(@RequestBody ExchangeDataQueryConditionDto condition) throws QCPromptException, SQLException {
        //判断输入条件是否正确，有些条件必须指定
        if (condition == null)
            throw new QCPromptException("查询数据指定的条件不能为空");
        if (condition.getCid() < 0x1)
            throw new QCPromptException("查询数据指定的数据分类ID错误");
        if (StringUtils.isBlank(condition.getSql()))
            throw new QCPromptException("查询数据指定的查询语句代码不能为空");
        if (StringUtils.isBlank(condition.getBtm()))
            throw new QCPromptException("查询数据指定的起始时间不能为空");
        if (StringUtils.isBlank(condition.getEtm()))
            throw new QCPromptException("查询数据指定的截止时间不能为空");
        //转换时间
        Date beginTime = DateUtil.parseDate(condition.getBtm());
        Date endTime = DateUtil.parseDate(condition.getEtm());
        if (beginTime == null)
            throw new QCPromptException("查询数据指定的起始时间错误，不是有效的时间");
        if (endTime == null)
            throw new QCPromptException("查询数据指定的截止时间错误，不是有效的时间");
        if (beginTime.after(endTime))
            throw new QCPromptException("查询数据指定的时间错误，起始时间不能大于截止时间");
        //转换站点编码
        String[] stcds = StringSplitUtil.Split(condition.getStcd(), true);

        //需要先根据交换数据分类ID获取对应的数据库表，再根据数据库表查询出表上配置的SQL语句，根据传入的sql语句编码确定本次使用的sql语句
        String tableCode = categoryService.getTableCode(condition.getCid());
        if (StringUtils.isBlank(tableCode))
            throw new QCPromptException("根据数据分类ID得到的数据库表代码为空，指定的数据分类ID错误或系统配置错误");
        //根据数据库表和查询语句代码得到数据库表信息（包含列信息、sql语句等信息）
        //根据数据库表编码获取完整信息，再从信息中获取sql语句、查询数据库连接信息和列信息
        DBTableFullDto tableFullInfo = restTemplatePlatform.getDbTableFullInfo(tableCode);
        String sql = "";// getDbTableSqlString(tableCode, condition.getSql());
        //数据库连接信息
        String dbUrl = "";
        String dbUser = "";
        String dbPassword = "";
        if (tableFullInfo != null) {
            List<DBTableSqlDto> tableSqls = tableFullInfo.getSqls();
            if (tableSqls != null && tableSqls.size() > 0x0) {
                for (DBTableSqlDto sqlDto : tableSqls) {
                    if (condition.getSql().equalsIgnoreCase(sqlDto.getCode())) {
                        sql = sqlDto.getSqlstr();
                        break;
                    }
                }
            }
            if (tableFullInfo.getDb() != null) {
                //解析数据库连接信息
                String dbConJsonString = tableFullInfo.getDb().getCon();
                if (StringUtils.isNotBlank(dbConJsonString)) {
                    //Json对象进行获取
                    dbUrl = JsonParserUtil.getString(dbConJsonString, "url");
                    dbUser = JsonParserUtil.getString(dbConJsonString, "user");
                    dbPassword = JsonParserUtil.getString(dbConJsonString, "password");
                }
            }
        }

        List<Map<String, Object>> queryResult = exchangeDataService.query(dbUrl, dbUser, dbPassword, sql, stcds, beginTime, endTime);
        //查询结果中List中的项为数据库表数据行，每行中的每列数据在Map中，Map中key为列名，value为列的数据值
        //处理查询结果，按前端返回数据结果进行转换
        ExchangeDataQueryQcTableResultDto result = new ExchangeDataQueryQcTableResultDto();
        //如果查询结果中没有任何行，也不需要返回查询列信息和站点信息
        if (queryResult != null && queryResult.size() > 0x0) {
            QCTable datatable = new QCTable();
            //使用第1行数据解析列名
            List<QCTableCell> headerRowCells = new ArrayList<>();
            Iterator<Map.Entry<String, Object>> firstRowIterator = queryResult.get(0x0).entrySet().iterator();
            while (firstRowIterator.hasNext()) {
                Map.Entry<String, Object> entry = firstRowIterator.next();
                //entry中的顺序与sql语句中指定的顺序不一致，原因未知
                System.out.println("entry.key:" + entry.getKey() + " entry.value:" + entry.getValue().toString());
                QCTableCell cell = new QCTableCell();
                //固定都是占1行1列
                cell.setRowspan(0x1);
                cell.setColspan(0x1);
                cell.setText(entry.getKey());

                headerRowCells.add(cell);
            }
            //默认都只有单行表头
            QCTableRow headerRow = new QCTableRow();
            headerRow.setCells(headerRowCells);
            List<QCTableRow> headerRows = new ArrayList<>();
            headerRows.add(headerRow);
            datatable.setHeaderRows(headerRows);

            //遍历行
            List<QCTableRow> bodyRows = new ArrayList<>();
            for (Map<String, Object> datarow : queryResult) {
                List<QCTableCell> bodyRowCells = new ArrayList<>();
                //System.out.println("listItem");
                Iterator<Map.Entry<String, Object>> iterator = datarow.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<String, Object> entry = iterator.next();
                    //System.out.println("entry.key:" + entry.getKey() + " entry.value:" + entry.getValue().toString());
                    QCTableCell cell = new QCTableCell();
                    //固定都是占1行1列
                    cell.setRowspan(0x1);
                    cell.setColspan(0x1);
                    cell.setText(entry.getValue().toString());

                    bodyRowCells.add(cell);
                }
                QCTableRow bodyRow = new QCTableRow();
                bodyRow.setCells(bodyRowCells);
                bodyRows.add(bodyRow);
            }
            datatable.setBodyRows(bodyRows);

            result.setData(datatable);
        }

        return result;
    }

    /***
     * 查询交换数据--返回原始结果类型，List<Map<String, Object>>
     *
     * @param condition 查询条件
     * @return qc.module.qms.dto.exchangeData.ExchangeDataQueryResultDto
     * @author QuCheng Tech
     * @since 2024/1/28
     */
    @RequestMapping(value = "/queryraw", method = {RequestMethod.POST, RequestMethod.GET})
    public ExchangeDataQueryRawDataResultDto queryRawDataResult(@RequestBody ExchangeDataQueryConditionDto condition) throws QCPromptException, SQLException, ParseException {
        //判断输入条件是否正确，有些条件必须指定
        if (condition == null)
            throw new QCPromptException("查询数据指定的条件不能为空");

        ExchangeDataQueryRawDataResultDto result = queryRawData(condition.getCid(), condition.getSql(), condition.getBtm(), condition.getEtm(), condition.getStcd());

        //如果查询的结果列集合不为空，数据行集合不为空，进行统计结果计算
        if (result != null && result.getColumns() != null && result.getColumns().size() > 0x0 && result.getData() != null && result.getData().size() > 0x0) {
            Date beginTime = DateUtil.parseDate(condition.getBtm());
            Date endTime = DateUtil.parseDate(condition.getEtm());
            /* 处理交换数据统计结果 */
            if (Objects.nonNull(condition.getSts())) {
                ExchangeDataStatisticsResultDto sts = statistics(beginTime, endTime, result.getData(), result.getColumns(), condition.getSts());
                if (Objects.nonNull(sts)) result.setSts(sts);
            }
        }

        //如果只统计，则只返回统计结果
        if (condition.isOnlyStatics()) {
            result.setData(null);
            result.setColumns(null);
            result.setStations(null);
        }

        return result;
    }

    /**
     * 按指定的时间规整数据
     * 指定1种时段类型和长度进行规则；
     * 数据值处理的方法根据数据库表列中的配置，对应时间点不存在的数据处理方式也是由数据库表内种的配置确定
     *
     * @param condition 查询条件
     * @return qc.module.qms.dto.exchange.data.ExchangeDataQueryRawDataResultDto
     * @author QuCheng Tech
     * @since 2025/1/8
     */
    @RequestMapping(value = "/regular", method = {RequestMethod.POST, RequestMethod.GET})
    public ExchangeDataQueryRawDataResultDto regularByTime(@RequestBody ExchangeDataRegularByTimeQueryConditionDto condition) throws QCPromptException, SQLException {
        //判断输入条件是否正确，有些条件必须指定
        if (condition == null)
            throw new QCPromptException("查询数据指定的条件不能为空");
        //判断传入的时间段类型是否正确
        TimeRangeLengthEnum timeRangeLengthEnum = getTimeRangeLengthEnum(condition.getIntervaltype());
        if (timeRangeLengthEnum == null || timeRangeLengthEnum == TimeRangeLengthEnum.CUSTOM)
            throw new QCPromptException("查询数据指定的条件中段次类型不能为空或指定错误");
        if (condition.getIntlength() < 0x1)
            throw new QCPromptException("查询数据指定的条件中段次长度必须为正整数");

        ExchangeDataQueryRawDataResultDto result = queryRawData(condition.getCid(), condition.getSql(), condition.getBtm(), condition.getEtm(), condition.getStcd());

        //如果查询的结果列集合不为空，数据行集合不为空，进行按传入的时间规整
        if (result != null && result.getColumns() != null && result.getColumns().size() > 0x0 && result.getData() != null && result.getData().size() > 0x0) {
            //获取查询数据结果中的时间列和数据值列集合
            DBTableColumnDto tmColumn = null;
            List<DBTableColumnDto> valueColumns = new ArrayList<>();
            //循环columns，找出数据记录统计结果需要的列编码
            for (DBTableColumnDto columnDto : result.getColumns()) {
                //找到列类型为32-数据时标列对象，获取数据时标列编码
                if (columnDto.getColtype() == ColumnTypeEnum.TIME_STAMP.getIndex()) {
                    tmColumn = columnDto;
                } else {
                    //找到列类型为数据值的列
                    if (columnDto.getColtype() == ColumnTypeEnum.VALUE.getIndex()) {
                        valueColumns.add(columnDto);
                    }
                }
            }
            if (tmColumn == null)
                throw new QCPromptException("查询结果中不存在时间列信息，无法进行按时间规整处理");
            if (valueColumns == null || valueColumns.size() < 0x1)
                throw new QCPromptException("查询结果中不存在任何数据值列，无法进行按时间规整处理");

            //由于查询得到的原始数据行集合中的类型均为Object，时间列无法根据时间进行筛选，数据值列也无法进行直接计算
            //先对查询得到的原始数据行集合中的数据类型进行转换处理
            List<QmsDataDto> qmsDatas = new ArrayList<>();
            for (Map<String, Object> datarow : result.getData()) {
                Date tm = DateUtil.parseDate(datarow.get(tmColumn.getCode()));

                for (DBTableColumnDto columnDto : valueColumns) {
                    Object objValue = datarow.get(columnDto.getCode());
                    if (objValue != null && StringUtils.isNotBlank(objValue.toString())) {
                        Double value = Double.parseDouble(objValue.toString());

                        QmsDataDto d = new QmsDataDto();
                        d.setCode(columnDto.getCode());
                        d.setTm(tm);
                        d.setValue(value);

                        qmsDatas.add(d);
                    }
                }
            }

            List<Map<String, Object>> regularByTimeDataRows = new ArrayList<>();
            //按时间规整后的数据集合，方便后面进行数据值特征值的统计
            List<QmsDataDto> regularByTimeQmsDatas = new ArrayList<>();

            Date beginTime = DateUtil.parseDate(condition.getBtm());
            Date endTime = DateUtil.parseDate(condition.getEtm());
            //按时间规整以传入的起始时间点所在的周期为开始，以传入的截止时间点所在的周围为结束
            Date intervalBeginTime = beginTime;
            Date intervalEndTime = endTime;
            switch (timeRangeLengthEnum) {
                case MINUTE:
                    intervalBeginTime = TimeSpanUtil.getBeginTime(beginTime, condition.getIntlength());
                    intervalEndTime = TimeSpanUtil.getEndTime(endTime, condition.getIntlength());
                    break;
                case HOUR:
                    //段次为小时时，固定以60分钟为分隔
                    intervalBeginTime = TimeSpanUtil.getBeginTime(beginTime, 60);
                    intervalEndTime = TimeSpanUtil.getEndTime(endTime, 60);
                    break;
                case DAY:
                    intervalBeginTime = DateUtil.getDayDate(beginTime);
                    intervalEndTime = DateUtil.getDayDate(endTime);
                    break;
                case WEEK:
                    //默认一周从星期一开始
                    Date beginTimeDate = DateUtil.getDayDate(beginTime);
                    Date endTimeDate = DateUtil.getDayDate(endTime);
                    int dayOfWeek = DateUtil.getDayOfWeek(beginTimeDate, true);
                    intervalBeginTime = DateUtil.addDays(beginTimeDate, dayOfWeek - 1);
                    dayOfWeek = DateUtil.getDayOfWeek(endTimeDate, true);
                    intervalEndTime = DateUtil.addDays(endTimeDate, 7 - dayOfWeek);
                    break;
                case TEN_DAYS:
                    intervalBeginTime = DateUtil.newDate(DateUtil.getYear(beginTime), DateUtil.getMonth(beginTime), DateUtil.getDay(beginTime), false);
                    intervalEndTime = DateUtil.newDate(DateUtil.getYear(endTime), DateUtil.getMonth(endTime), DateUtil.getDay(endTime), false);
                    break;
                case MONTH:
                    intervalBeginTime = DateUtil.newDate(DateUtil.getYear(beginTime), DateUtil.getMonth(beginTime), 0x1, false);
                    intervalEndTime = DateUtil.newDate(DateUtil.getYear(endTime), DateUtil.getMonth(endTime), 0x1, false);
                    break;
                case QUARTER:
                    int beginTimeMonth = DateUtil.getMonth(beginTime);
                    if (beginTimeMonth >= 10)
                        intervalBeginTime = DateUtil.newDate(DateUtil.getYear(beginTime), 10, 0x1, false);
                    else if (beginTimeMonth >= 7)
                        intervalBeginTime = DateUtil.newDate(DateUtil.getYear(beginTime), 7, 0x1, false);
                    else if (beginTimeMonth >= 4)
                        intervalBeginTime = DateUtil.newDate(DateUtil.getYear(beginTime), 4, 0x1, false);
                    else
                        intervalBeginTime = DateUtil.newDate(DateUtil.getYear(beginTime), 1, 0x1, false);
                    int endTimeMonth = DateUtil.getMonth(endTime);
                    if (endTimeMonth >= 10)
                        intervalEndTime = DateUtil.newDate(DateUtil.getYear(endTime), 10, 0x1, false);
                    else if (endTimeMonth >= 7)
                        intervalEndTime = DateUtil.newDate(DateUtil.getYear(endTime), 7, 0x1, false);
                    else if (endTimeMonth >= 4)
                        intervalEndTime = DateUtil.newDate(DateUtil.getYear(endTime), 4, 0x1, false);
                    else
                        intervalEndTime = DateUtil.newDate(DateUtil.getYear(endTime), 1, 0x1, false);
                    break;
                case YEAR:
                    intervalBeginTime = DateUtil.newDate(DateUtil.getYear(beginTime), 0x1, 0x1, false);
                    intervalEndTime = DateUtil.newDate(DateUtil.getYear(endTime), 0x1, 0x1, false);
                    break;
                default:
                    break;
            }

            System.out.println("regularByTime 计算最终所需的处理时段时间为：" + DateUtil.getDateTimeString(intervalBeginTime) + "  -  " + DateUtil.getDateTimeString(intervalEndTime));

            //遍历得到的起始时间段的时间点，逐个时间点进行处理
            Date intervalTime = intervalBeginTime;
            while (intervalTime.after(intervalEndTime) == false) {
                //计算得到下一个时段的时间--后面获取时段内的数据需要使用该时间
                Date nextIntervalTime = intervalTime;
                //时间段往后移N个时段长
                switch (timeRangeLengthEnum) {
                    case MINUTE:
                        nextIntervalTime = DateUtil.addMinutes(intervalTime, condition.getIntlength());
                        break;
                    case HOUR:
                        nextIntervalTime = DateUtil.addHours(intervalTime, condition.getIntlength());
                        break;
                    case DAY:
                        nextIntervalTime = DateUtil.addDays(intervalTime, condition.getIntlength());
                        break;
                    case WEEK:
                        nextIntervalTime = DateUtil.addDays(intervalTime, condition.getIntlength() * 0x7);
                        break;
                    case TEN_DAYS:
                        nextIntervalTime = DateUtil.addTendays(intervalTime, condition.getIntlength());
                        break;
                    case MONTH:
                        nextIntervalTime = DateUtil.addMonths(intervalTime, condition.getIntlength());
                        break;
                    case QUARTER:
                        nextIntervalTime = DateUtil.addMonths(intervalTime, condition.getIntlength() * 0x3);
                        break;
                    case YEAR:
                        nextIntervalTime = DateUtil.addYears(intervalTime, condition.getIntlength());
                        break;
                    default:
                        break;
                }

                System.out.println("regularByTime 遍历时段处理，当前处理时段时间为：" + DateUtil.getDateTimeString(intervalTime) + "  -  " + DateUtil.getDateTimeString(nextIntervalTime));

                //初始化一个时段数据，先对时段的时间赋值，循环数据值列获取原始值并计算后对数据值赋值
                Map<String, Object> row = new HashMap<>();
                row.put(tmColumn.getCode(), intervalTime);

                Date finalIntervalTime = intervalTime;
                Date finalNextIntervalTime = nextIntervalTime;
                //获取时间段内的原始数据行
                for (DBTableColumnDto columnDto : valueColumns) {
                    //指定列指定一个时段的数值，默认为null
                    OptionalDouble intervalValue = null;
                    //根据数据列名和时间段筛选数据
                    List<QmsDataDto> filterDatas = qmsDatas.stream().filter(p -> StringUtils.equalsIgnoreCase(columnDto.getCode(), p.getCode())
                            && p.getTm().before(finalIntervalTime) == false && p.getTm().after(finalNextIntervalTime) == false
                            && p.getValue() != null && Double.isNaN(p.getValue().doubleValue()) == false).collect(Collectors.toList());
                    if (filterDatas != null && filterDatas.size() > 0x0) {
                        //根据列的配置计算数据值和进行对应时间点缺数的处理
                        //暂时按照计算平均值处理
                        intervalValue = filterDatas.stream().mapToDouble(p -> p.getValue()).average();
                    } else {
                        //如果对应的时间段内缺数，根据列的配置进行处理；可能该时间段内的数值设置为0或取最近1条数据
                    }

                    if (intervalValue != null && intervalValue.isPresent() && Double.isNaN(intervalValue.getAsDouble()) == false) {
                        double v = intervalValue.getAsDouble();

                        row.put(columnDto.getCode(), v);

                        QmsDataDto d = new QmsDataDto();
                        d.setCode(columnDto.getCode());
                        d.setTm(finalIntervalTime);
                        d.setValue(v);
                        regularByTimeQmsDatas.add(d);
                    }
                }

                regularByTimeDataRows.add(row);

                intervalTime = nextIntervalTime;
            }

            result.setData(regularByTimeDataRows);

            //按时间规整后的数据进行数据值的特征值统计
            if (regularByTimeQmsDatas != null && regularByTimeQmsDatas.size() > 0x0) {

            }

            return result;
        }

        return null;
    }

    TimeRangeLengthEnum getTimeRangeLengthEnum(int index) {
        if (index > 0x0) {
            for (TimeRangeLengthEnum item : TimeRangeLengthEnum.values()) {
                if (item.getIndex() == index) {//判断序号使用getIndex，判断内容使用getName
                    return item;
                }
            }
        }
        return TimeRangeLengthEnum.CUSTOM;
    }

    /**
     * 根据指定的查询条件得到交换数据原始结果，原始结果中包含列集合、站点信息集合、原始数据（以行--列结构返回的数据库表中的数据）
     *
     * @param cid     交换数据分类ID
     * @param sqlCode 查询语句代码
     * @param btm     起始时间
     * @param etm     截止是啊金
     * @param stcd    站点编码，可以为1个或多个，多个使用逗号分隔
     * @return qc.module.qms.dto.exchange.data.ExchangeDataQueryRawDataResultDto
     * @author QuCheng Tech
     * @since 2025/1/8
     */
    ExchangeDataQueryRawDataResultDto queryRawData(int cid, String sqlCode, String btm, String etm, String stcd) throws QCPromptException, SQLException {
        //判断输入条件是否正确，有些条件必须指定
        if (cid < 0x1)
            throw new QCPromptException("查询数据指定的数据分类ID错误");
        if (StringUtils.isBlank(sqlCode))
            throw new QCPromptException("查询数据指定的查询语句代码不能为空");
        if (StringUtils.isBlank(btm))
            throw new QCPromptException("查询数据指定的起始时间不能为空");
        if (StringUtils.isBlank(etm))
            throw new QCPromptException("查询数据指定的截止时间不能为空");
        //转换时间
        Date beginTime = DateUtil.parseDate(btm);
        Date endTime = DateUtil.parseDate(etm);
        if (beginTime == null)
            throw new QCPromptException("查询数据指定的起始时间错误，不是有效的时间");
        if (endTime == null)
            throw new QCPromptException("查询数据指定的截止时间错误，不是有效的时间");
        if (beginTime.after(endTime))
            throw new QCPromptException("查询数据指定的时间错误，起始时间不能大于截止时间");
        //判断站点编码
        if (StringUtils.isBlank(stcd))
            throw new QCPromptException("查询数据指定的站点编码不能为空");
        //转换站点编码
        String[] stcds = StringSplitUtil.Split(stcd, true);
        if (stcds == null || stcds.length < 0x1)
            throw new QCPromptException("查询数据指定的站点编码集合不能为空");

        //需要先根据交换数据分类ID获取对应的数据库表，再根据数据库表查询出表上配置的SQL语句，根据传入的sql语句编码确定本次使用的sql语句
        String tableCode = categoryService.getTableCode(cid);
        if (StringUtils.isBlank(tableCode))
            throw new QCPromptException("根据数据分类ID得到的数据库表代码为空，指定的数据分类ID错误或系统配置错误");
        //根据数据库表编码获取完整信息，再从信息中获取sql语句、查询数据库连接信息和列信息
        DBTableFullDto tableFullInfo = restTemplatePlatform.getDbTableFullInfo(tableCode);
        String sql = "";// getDbTableSqlString(tableCode, condition.getSql());
        //数据库连接信息
        String dbUrl = "";
        String dbUser = "";
        String dbPassword = "";
        if (tableFullInfo != null) {
            List<DBTableSqlDto> tableSqls = tableFullInfo.getSqls();
            if (tableSqls != null && tableSqls.size() > 0x0) {
                for (DBTableSqlDto sqlDto : tableSqls) {
                    if (sqlCode.equalsIgnoreCase(sqlDto.getCode())) {
                        sql = sqlDto.getSqlstr();
                        break;
                    }
                }
            }
            if (tableFullInfo.getDb() != null) {
                //解析数据库连接信息
                String dbConJsonString = tableFullInfo.getDb().getCon();
                if (StringUtils.isNotBlank(dbConJsonString)) {
                    //Json对象进行获取
                    dbUrl = JsonParserUtil.getString(dbConJsonString, "url");
                    dbUser = JsonParserUtil.getString(dbConJsonString, "user");
                    dbPassword = JsonParserUtil.getString(dbConJsonString, "password");
                }
            }
        }
        if (StringUtils.isBlank(sql))
            throw new QCPromptException("查询数据指定的查询语句代码不得到的查询sql为空");

        List<Map<String, Object>> queryResult = exchangeDataService.query(dbUrl, dbUser, dbPassword, sql, stcds, beginTime, endTime);

        ExchangeDataQueryRawDataResultDto result = new ExchangeDataQueryRawDataResultDto();
        result.setData(queryResult);
        //设置返回的站点信息
        //根据分类ID筛选站点编码，先根据交换数据分类ID获取所有站点，如果传入的站点编码不为空根据传入的进行筛选
        List<StationDto> stations = null;
        if (stcds != null && stcds.length > 0x0)
            stations = stationService.getByFilterStationCodes(stcds);
        else
            stations = stationService.getByCategoryId(cid);
        result.setStations(stations);
        //设置返回的列信息，不管是否有查询结果数据均需要返回列信息
        System.out.println("getQueryFieldNamesFromSql sql=" + sql);
        LinkedHashMap<String, String> queryFields = (queryResult != null && queryResult.size() > 0x0) ? getQueryFieldNamesFromResultData(queryResult.get(0x0)) : getQueryFieldNamesFromSqlByJSqlParser(sql);
        //LinkedHashMap<String, String> queryFields = getQueryFieldNamesFromSql(sql);
        if (queryFields != null && queryFields.size() > 0x0) {
            //获取数据库表的列信息进行逐个处理
            //使用数据库表列信息遍历可能会出现查询结果列不在定义的列中导致无法处理，直接使用查询结果列又会出现列的先后顺序不是逾期
            //在此单独构造出一个返回结果列集合，先按照数据库表列顺序（要求查询结果中包含当前列），再加入查询结果返回列（数据库表列定义中没有的）
            List<String> queryResultFieldInColumns = new ArrayList<>();
            List<String> queryResultFieldNotInColumns = new ArrayList<>();
            List<DBTableColumnDto> tableColumns = tableFullInfo.getColumns();
            if (tableColumns != null && tableColumns.size() > 0x0) {
                //有定义数据库表列信息，根据查询结果列判断是否需要在返回列信息中
                for (String queryResultFieldName : queryFields.keySet()) {
                    //判断查询结果列是否在数据库表列定义中
                    Optional<DBTableColumnDto> selectedColumn = tableColumns.stream().filter(p -> p.getCode().equalsIgnoreCase(queryResultFieldName)).findFirst();
                    if (selectedColumn != null && selectedColumn.isPresent()) {
                        queryResultFieldInColumns.add(queryResultFieldName);
                    } else {
                        queryResultFieldNotInColumns.add(queryResultFieldName);
                    }
                }
            } else {
                //如果没有定义数据库表列信息，全部使用查询结果中的返回列
                queryResultFieldNotInColumns = queryFields.keySet().stream().collect(Collectors.toList());
            }

            //按照查询sql语句中的字段顺序进行返回，如果在列集合中没有对应的信息使用初始值
            List<DBTableColumnDto> columns = new ArrayList<>();
            //列集合结果的顺序按照数据库表列配置的顺序；先从数据库表列配置的集合中遍历添加，再从没有对应数据库表列配置的集合中遍历添加
            if (queryResultFieldInColumns != null && queryResultFieldInColumns.size() > 0x0) {
                for (DBTableColumnDto tableColumn : tableColumns) {
                    //遍历数据库表列集合，保证顺序是按照固定的顺序返回
                    Optional<String> selectedColumn = queryResultFieldInColumns.stream().filter(p -> p.equalsIgnoreCase(tableColumn.getCode())).findFirst();
                    if (selectedColumn != null && selectedColumn.isPresent()) {
                        //如果列在查询返回结果中存在，直接使用数据库表列信息
                        columns.add(tableColumn);
                    }
                }
            }
            //遍历不在数据库表列中的列名进行添加
            if (queryResultFieldNotInColumns != null && queryResultFieldNotInColumns.size() > 0x0) {
                for (String fieldName : queryResultFieldNotInColumns) {
                    DBTableColumnDto column = new DBTableColumnDto();
                    column.setCode(fieldName);
                    //没有对应的列信息，使用默认值
                    column.setDatatype(DataTypeEnum.STRING.getIndex());
                    column.setName(fieldName);
                    //列的属性中默认值设置，否则在界面中无法显示
                    column.setQrshow(true);
                    column.setEfshow(true);

                    columns.add(column);
                }
            }
            result.setColumns(columns);
        }

        return result;
    }

    /***
     * 查询数据交换日志
     *
     * @param condition 查询条件
     * @return java.util.List<qc.module.qms.dto.exchange.log.DataExchangeLogDto>
     * @author QuCheng Tech
     * @since 2024/9/25
     */
    @RequestMapping(value = "/logs", method = {RequestMethod.POST, RequestMethod.GET})
    public List<DataExchangeLogDto> queryLogs(@RequestBody DataExchangeLogQueryConditionDto condition) throws QCPromptException {
        return exchangeLogService.queryLogs(condition);
    }

    /***
     * 人工操作交换数据，可以对交换数据进行重新生成、修改和删除
     *
     * @param dto 操作DTO
     * @return java.lang.String
     * @author QuCheng Tech
     * @since 2024/9/25
     */
    @RequestMapping(value = "/manual", method = {RequestMethod.POST, RequestMethod.GET})
    public String manualOperate(@RequestBody DataExchangeLogDto dto) throws QCPromptException {
        if (dto == null)
            return QCUnifyReturnValue.Warn("人工操作交换数据的对象不能为空");
        //获取用户名称
        HttpHeaders headers = new HttpHeaders();
        headers.add(QCAuthConstant.TOKEN_HEADER_KEY, request.getHeader(QCAuthConstant.TOKEN_HEADER_KEY));
        headers.add(QCAuthConstant.PROJECT_CODE_HEADER_KEY, request.getHeader(QCAuthConstant.PROJECT_CODE_HEADER_KEY));
        UacUserInfoDto userInfoDto = restTemplatePlatform.getUserInfo(headers);
        if (Objects.nonNull(userInfoDto)) {
            dto.setUsername(userInfoDto.getName());
        }

        return exchangeLogService.addLog(dto);
    }

    /***
     * 取消指定的交换数据人工操作
     *
     * @param id 日志ID
     * @return java.lang.String
     * @author QuCheng Tech
     * @since 2024/9/25
     */
    @RequestMapping(value = "/cancel/{id}", method = {RequestMethod.POST, RequestMethod.GET})
    public String cancelByPath(@PathVariable(required = true) String id) {
        return exchangeLogService.cancel(id);
    }

    /***
     * 取消指定的交换数据人工操作
     *
     * @param id 日志ID
     * @return java.lang.String
     * @author QuCheng Tech
     * @since 2024/9/25
     */
    @RequestMapping(value = "/cancel", method = {RequestMethod.POST, RequestMethod.GET})
    public String cancelByParam(@RequestParam(required = true) String id) {
        return exchangeLogService.cancel(id);
    }

    /**
     * 交换数据统计
     *
     * @param beginTime 开始时间条件
     * @param endTime   结束时间条件
     * @param data      数据集合
     * @param columns   列集合
     * @param condition 交换数据统计条件
     * @return ExchangeDataStatisticsResultDto 交换数据统计结果
     * @author QuCheng Tech
     * @since 2024/4/12
     */
    ExchangeDataStatisticsResultDto statistics(Date beginTime,
                                               Date endTime,
                                               List<Map<String, Object>> data,
                                               List<DBTableColumnDto> columns,
                                               DataRecordStatisticsConditionDto condition) throws ParseException {

        long ratestsBeginTime = System.currentTimeMillis();
        System.out.println("===到报准点统计开始时间====" + ratestsBeginTime);
        //如果结束时间 > 当前时间，结束时间使用当前时间
        Date currentDate = new Date();
        if (endTime.after(currentDate)) {
            endTime = currentDate;
        }

        //交换数据统计结果
        ExchangeDataStatisticsResultDto result = new ExchangeDataStatisticsResultDto();

        /* 数据记录统计结果 */
        DataRecordStatisticsResultDto recordStatisticsResult = new DataRecordStatisticsResultDto();
        /* 数据记录统计结果参数值初始化 */
        //设置到报率统计，默认为false
        boolean ratests = false;
        //设置准点率统计，默认为false
        boolean ontimests = false;
        //应有数据条数
        int duecount = 0;
        //实有数据条数
        int realcount = 0;
        //缺失数据条数
        int lostcount = 0;
        //缺失数据时间集合
        List<String> losttms = new ArrayList<>();
        //准点到报数据条数
        int ontimecount = 0;
        //误点到报数据条数
        int mistimecount = 0;
        //误点数据时间集合，key为数据时标时间，value为对应数据的写入时间
        List<Map<String, String>> mistms = new ArrayList<>();

        String tmCode = ""; //数据时标列编码
        String inTmCode = ""; //写入时间列编码

        //需要有数据频率才能统计到报和准点，数据频率必须同时有频率类型和频率数值
        if (Objects.nonNull(condition) && condition.getRatetype() > 0x0 && condition.getRatevalue() > 0x0) {
            //循环columns，找出数据记录统计结果需要的列编码
            for (DBTableColumnDto columnDto : columns) {
                //找到列类型为32-数据时标列对象，获取数据时标列编码
                if (columnDto.getColtype() == ColumnTypeEnum.TIME_STAMP.getIndex()) {
                    tmCode = columnDto.getCode();
                }
                //如果统计准点率，判断有没有写入时间列，没有就不统计
                if (condition.getOntime()) {
                    if (columnDto.getColtype() == ColumnTypeEnum.TIME_IN.getIndex()) {
                        inTmCode = columnDto.getCode();
                    }
                }
            }

            //必须要有数据时标列才能统计到报和准点
            //必须要有数据写入时间列才能统计准点
            if (StringUtils.isNotBlank(tmCode)) {
                ratests = true;
                if (condition.getOntime() && StringUtils.isNotBlank(inTmCode))
                    ontimests = true;
            }
        }

        //如果到报和准点统计至少有1个，进行统计处理
        //在处理前对查询出来的数据记录data中的时间提前进行转换处理，解决每次循环时转换出现的创建SimpleDateFormat对象耗时长的问题
        if (ratests == true || ontimests == true) {
            //从data中将数据时标列写入时间找出来存放在对应的map中，key存储数据时标时间，value存储对应的序号（从0开始）
            Map<Date, Integer> dataTimeAndIndexs = new HashMap<>();
            List<Date> intms = new ArrayList<>();
            if (CollectionUtils.isNotEmpty(data)) {
                for (int i = 0; i < data.size(); i++) {
                    Date tm = DateUtil.parseDate(data.get(i).get(tmCode));
                    Date inTm = DateUtil.parseDate(data.get(i).get(inTmCode));

                    dataTimeAndIndexs.put(tm, i);
                    intms.add(inTm);
                }
                //System.out.println("查询到数据记录数=" + data.size() + " 数据时标对应序号总数=" + dataTimeAndIndexs.size() + " 写入时间集合总数=" + intms.size());
            }

            //缺数时间格式化，可以根据传入的数据时间频率类型来
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

            //统计到报和准点使用的前后允许分钟数，默认为0，如果输入的有值使用输入的值
            Integer rateBeforeMinutes = 0x0;
            Integer rateAfterMinutes = 0x0;
            Integer ontimeBeforeMinutes = 0x0;
            Integer ontimeAfterMinutes = 0x0;
            if (condition.getRateafter() != null)
                rateAfterMinutes = Math.abs(condition.getRateafter());
            if (condition.getRatebefore() != null)
                rateBeforeMinutes = Math.abs(condition.getRatebefore());
            if (condition.getOntimebefore() != null)
                ontimeBeforeMinutes = Math.abs(condition.getOntimebefore());
            if (condition.getOntimeafter() != null)
                ontimeAfterMinutes = Math.abs(condition.getOntimeafter());

            //从起始时间到截止时间，2个时间点都包含
            while (beginTime.after(endTime) == false) {
                //根据频率允许提前分钟数、频率允许延后分钟数计算到报点有效范围
                Date rateBeginTm = DateUtil.addMinutes(beginTime, -rateBeforeMinutes);//减去ratebefore分钟
                Date rateEndTm = DateUtil.addMinutes(beginTime, rateAfterMinutes);//添加rateafter分钟

                Map<Date, Integer> filterByTime = null;
                if (dataTimeAndIndexs != null && dataTimeAndIndexs.size() > 0x0) {
                    //根据数据时标判断当前时间点数据是否有：不大于rateEndTm 并且 不小于rateBeginTm
                    filterByTime = dataTimeAndIndexs.entrySet().stream().filter(p -> p.getKey().before(rateBeginTm) == false && p.getKey().after(rateEndTm) == false).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
                }

                //畅通率统计需要先统计到报情况，因此不管是否统计畅通率均需要进行到报率统计
                //应有数据条数 + 1
                duecount += 1;
                //统计到报率相关数据：查找数据中当前时间点到报有效范围内的数据记录，有数据则实有数据条数+1，没有则缺失数据条数+1并记录缺失时间
                if (dataTimeAndIndexs != null && dataTimeAndIndexs.size() > 0x0) {
                    if (filterByTime != null && filterByTime.size() > 0x0) {
                        //当前时间点有对应的数据，实有数量+1
                        realcount += 1;
                        //有对应时间点的数据时才需要判断是否为准点
                        if (ontimests) {
                            //判断是否准点，使用写入时间与数据时标之间前后允许的分钟数
                            Date ontimeBeginTm = DateUtil.addMinutes(beginTime, -ontimeBeforeMinutes);
                            Date ontimeEndTm = DateUtil.addMinutes(beginTime, ontimeAfterMinutes);
                            //获取对应时间点数据时标，如果有多条只使用第1条数据的时标
                            for (Map.Entry<Date, Integer> entry : filterByTime.entrySet()) {
                                int dataIdx = entry.getValue().intValue();
                                //根据数据索引获取写入时间
                                Date dataInTime = intms.get(dataIdx);

                                //System.out.println("判断准点：时标=" + DateUtil.getDateTimeString(beginTime) + " 写入时间=" + DateUtil.getDateTimeString(dataInTime) + " 起始时间=" + DateUtil.getDateTimeString(ontimeBeginTm) + " 截止时间=" + DateUtil.getDateTimeString(ontimeEndTm));
                                //判断是否准点：不小于ontimeBeginTm 并且 不大于ontimeEndTm
                                if (dataInTime == null) {
                                    //如果写入时间为空，默认为误点数据
                                    //误点条数+1
                                    mistimecount += 1;
                                    Map<String, String> mistmMap = new HashMap<>();
                                    mistmMap.put(sdf.format(beginTime), "写入时间为空");
                                    mistms.add(mistmMap);
                                } else if (dataInTime.before(ontimeBeginTm) || dataInTime.after(ontimeEndTm)) {
                                    //如果写入时间在准点开始时间之前或者在准点截止时间之后，均算作误点数据
                                    //误点条数+1
                                    mistimecount += 1;
                                    Map<String, String> mistmMap = new HashMap<>();
                                    mistmMap.put(sdf.format(beginTime), sdf.format(dataInTime));
                                    mistms.add(mistmMap);
                                } else {
                                    //其他情况算作准点数据
                                    //准点条数+1
                                    ontimecount += 1;
                                }

                                break;
                            }
                        }
                    } else {
                        //当前时间点没有数据，缺失数量+1，缺失时间集合增加当前时间点
                        lostcount += 1;
                        losttms.add(sdf.format(beginTime));
                        //对应时间点的数据缺失时，畅通率统计不需要进行判断处理
                    }
                } else {
                    //如所有数据均没有，缺失数量+1，不需要记录缺失时间集合
                    lostcount += 1;
                    //所有时间点的数据缺失时，畅通率统计不需要进行判断处理
                }

                //根据不同时间频率类型和频率数值，计算下一个开始时间
                if (condition.getRatetype() == TimeRangeLengthEnum.YEAR.getIndex()) {
                    beginTime = DateUtil.addYears(beginTime, condition.getRatevalue());
                } else if (condition.getRatetype() == TimeRangeLengthEnum.QUARTER.getIndex()) {
                    //季度，月份+3
                    beginTime = DateUtil.addMonths(beginTime, condition.getRatevalue() * 0x3);
                } else if (condition.getRatetype() == TimeRangeLengthEnum.MONTH.getIndex()) {
                    beginTime = DateUtil.addMonths(beginTime, condition.getRatevalue());
                } else if (condition.getRatetype() == TimeRangeLengthEnum.TEN_DAYS.getIndex()) {
                    beginTime = DateUtil.addTendays(beginTime, condition.getRatevalue());
                } else if (condition.getRatetype() == TimeRangeLengthEnum.WEEK.getIndex()) {
                    //周，天数+7
                    beginTime = DateUtil.addDays(beginTime, condition.getRatevalue() * 0x7);
                } else if (condition.getRatetype() == TimeRangeLengthEnum.DAY.getIndex()) {
                    beginTime = DateUtil.addDays(beginTime, condition.getRatevalue());
                } else if (condition.getRatetype() == TimeRangeLengthEnum.HOUR.getIndex()) {
                    beginTime = DateUtil.addHours(beginTime, condition.getRatevalue());
                } else if (condition.getRatetype() == TimeRangeLengthEnum.MINUTE.getIndex()) {
                    beginTime = DateUtil.addMinutes(beginTime, condition.getRatevalue());
                } else {
                    break;
                }
            }

            //优化减少返回数据，如果数据全部缺失或全部误点，缺失和误点的时间集合设置为空
            if (duecount == lostcount)
                losttms = new ArrayList<>();
            if (duecount == mistimecount)
                mistms = new ArrayList<>();
        }

        /* 数据记录统计结果 */
        recordStatisticsResult.setRatests(ratests);
        recordStatisticsResult.setOntimests(ontimests);
        recordStatisticsResult.setDuecount(duecount);
        recordStatisticsResult.setRealcount(realcount);
        recordStatisticsResult.setLostcount(lostcount);
        recordStatisticsResult.setLosttms(losttms);
        recordStatisticsResult.setOntimecount(ontimecount);
        recordStatisticsResult.setMistimecount(mistimecount);
        recordStatisticsResult.setMistms(mistms);

        result.setRecordsts(recordStatisticsResult);

        long ratestsEndTime = System.currentTimeMillis();
        System.out.println("===到报准点统计结束时间====" + ratestsEndTime);
        System.out.println("到报准点统计消耗时间===" + (ratestsEndTime - ratestsBeginTime) + "ms");

        /* 数据值统计 */
        List<DataValueStatisticsResultDto> valuests = eigenValueStatistics(data, columns, tmCode);
        result.setValuests(valuests);

        /* 状态统计 */
        List<ExchangeDataStatustsDto> statusts = dictValueStatusStatistics(data, columns);
        result.setStatusts(statusts);

        return result;
    }

    /**
     * 数值的特征值统计；统计数据行中的平均值、合计值、差值、最值及时间
     *
     * @param data    数据集合
     * @param columns 列集合
     * @param tmCode  时间列编码
     * @return List<DataValueStatisticsResultDto> 数据值统计结果DTO集合
     * @author QuCheng Tech
     * @since 2024/4/15
     */
    List<DataValueStatisticsResultDto> eigenValueStatistics(List<Map<String, Object>> data, List<DBTableColumnDto> columns, String tmCode) {
        long ratestsBeginTime = System.currentTimeMillis();
        System.out.println("===数据值统计开始时间====" + ratestsBeginTime);
        /* 数据值统计 */
        List<DataValueStatisticsResultDto> valuests = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(data)) {
            if (CollectionUtils.isNotEmpty(columns)) {
                if (StringUtils.isNotBlank(tmCode)) {
                    for (DBTableColumnDto columnDto : columns) {
                        //如果列类型为40-数据值列，则统计相关数据
                        if (columnDto.getColtype() == ColumnTypeEnum.VALUE.getIndex()) {
                            DataValueStatisticsResultDto dataValueStsDto = new DataValueStatisticsResultDto();
                            dataValueStsDto.setName(columnDto.getName());
                            dataValueStsDto.setUnit(columnDto.getUnit());
                            int count = 0;//数据条数
                            double sumv = 0.0d;//合计值
                            //最大值、最大值时间、最小值、最小值时间默认设置为第一条数据的值
                            double beforeValue = Double.NaN;
                            double maxv = Double.NaN;
                            double minv = Double.NaN;
                            String maxtm = "";
                            String mintm = "";
                            //遍历每条数据进行判断处理
                            for (int i = 0; i < data.size(); i++) {
                                //只要有数据，总数量加1
                                count += 1;
                                //判断数据值列是否有效，不能为空或无法转换为double类型
                                Object dataValueObj = data.get(i).get(columnDto.getCode());
                                if (dataValueObj != null) {
                                    //不能为空字符串
                                    if (StringUtils.isNotEmpty(dataValueObj.toString())) {
                                        //必须能转换为有效的double
                                        try {
                                            double currentValue = Double.parseDouble(dataValueObj.toString());
                                            //判断当前值不能是NaN
                                            if (Double.isNaN(currentValue) == false) {
                                                //累计值累加
                                                sumv += currentValue;

                                                String currentTmStr = data.get(i).get(tmCode).toString();
                                                //处理最值：如果前一条数据为NaN直接将当前数据作为最值，如果前一条数据不为NaN进行判断
                                                if (Double.isNaN(beforeValue)) {
                                                    maxv = currentValue;
                                                    maxtm = currentTmStr;
                                                    minv = currentValue;
                                                    mintm = currentTmStr;
                                                } else {
                                                    //前一条数据数值有效时最值必然已经有值，比较最值和当前值的大小；如果数值相等保持原有的最值
                                                    if (currentValue < minv) {
                                                        minv = currentValue;
                                                        mintm = currentTmStr;
                                                    }
                                                    if (currentValue > maxv) {
                                                        maxv = currentValue;
                                                        maxtm = currentTmStr;
                                                    }
                                                }

                                                //更新记录的前一条数据值为当前数值
                                                beforeValue = currentValue;
                                            }
                                        } catch (NumberFormatException e) {
                                            //转换为double异常不进行处理
                                        }
                                    }
                                }
                            }
                            //获取列的格式化字符串
                            String formarStr = "";
                            if (StringUtils.isNotBlank(columnDto.getFormatstr())) {
                                formarStr = columnDto.getFormatstr();
                            }
                            //平均值
                            double avgv = sumv / count;
                            //差值
                            double diffv = maxv - minv;
                            dataValueStsDto.setSumv(NumberUtil.parseNumberToString(sumv, formarStr));
                            dataValueStsDto.setAvgv(NumberUtil.parseNumberToString(avgv, formarStr));
                            dataValueStsDto.setMaxv(NumberUtil.parseNumberToString(maxv, formarStr));
                            dataValueStsDto.setMaxtm(maxtm);
                            dataValueStsDto.setMinv(NumberUtil.parseNumberToString(minv, formarStr));
                            dataValueStsDto.setMintm(mintm);
                            dataValueStsDto.setDiffv(NumberUtil.parseNumberToString(diffv, formarStr));
                            valuests.add(dataValueStsDto);
                        }
                    }
                }
            }
        }
        long ratestsEndTime = System.currentTimeMillis();
        System.out.println("===数据值统计结束时间====" + ratestsEndTime);
        System.out.println("======数据值统计消耗时间===" + (ratestsEndTime - ratestsBeginTime) + "ms");
        return valuests;
    }

    /**
     * 列类型为字典或枚举时统计不同取值表示状态数量统计
     *
     * @param data    数据集合
     * @param columns 列集合
     * @return List<KeyValuePairDto> 状态统计集合
     * @author QuCheng Tech
     * @since 2024/4/15
     */
    List<ExchangeDataStatustsDto> dictValueStatusStatistics(List<Map<String, Object>> data, List<DBTableColumnDto> columns) {
        //返回结果集
        List<ExchangeDataStatustsDto> statusts = new ArrayList<>();

        long ratestsBeginTime = System.currentTimeMillis();
        System.out.println("===状态统计开始时间====" + ratestsBeginTime);
        if (CollectionUtils.isNotEmpty(data)) {
            for (DBTableColumnDto columnDto : columns) {

                if (columnDto.getDatatype() != DataTypeEnum.INT.getIndex() && columnDto.getDatatype() != DataTypeEnum.BOOL.getIndex()) {
                    continue;
                }

                //在列类型为：70-字典、71-枚举字典、72-数据字典时均进行处理；
                if (columnDto.getColtype() == ColumnTypeEnum.DICT.getIndex() ||
                        columnDto.getColtype() == ColumnTypeEnum.DICT_ENUM.getIndex() ||
                        columnDto.getColtype() == ColumnTypeEnum.DICT_DEFINE.getIndex()) {
                    if (columnDto.getOptions() != null && columnDto.getOptions().size() > 0x0) {
                        ExchangeDataStatustsDto statustsDto = new ExchangeDataStatustsDto();
                        List<KeyValuePairDto> colOptions = new ArrayList<>();
                        //记录统计为0的状态数量
                        int isZeroCount = 0x0;
                        //如果列的数据类型为整数，进行数据类型转换处理
                        if (columnDto.getDatatype() == DataTypeEnum.INT.getIndex()) {
                            for (KeyValuePairDto keyValuePairDto : columnDto.getOptions()) {
                                int count = 0;
                                for (Map<String, Object> map : data) {
                                    if (Integer.parseInt(keyValuePairDto.getKey()) == Integer.parseInt(map.get(columnDto.getCode()).toString())) {
                                        count++;
                                    }
                                }
                                if (count == 0x0) isZeroCount++;
                                KeyValuePairDto colOption = new KeyValuePairDto();
                                colOption.setKey(keyValuePairDto.getValue());
                                colOption.setValue(String.valueOf(count));
                                colOptions.add(colOption);
                            }
                            statustsDto.setKey(columnDto.getName());
                            //statustsDto.setValue(colOptions);
                            //一个状态列中将状态统计为0的合并显示为：其他（n），n表示统计为0的状态数量；如果n为1，就不需要合并显示；
                            if (isZeroCount > 0x1) {
                                statustsDto.setValue(mergeOptions(colOptions, isZeroCount));
                            } else {
                                statustsDto.setValue(colOptions);
                            }
                            statusts.add(statustsDto);
                        }
                        //如果列的数据类型为布尔
                        else if (columnDto.getDatatype() == DataTypeEnum.BOOL.getIndex()) {
                            for (int i = 0; i < columnDto.getOptions().size(); i++) {
                                int count = 0;
                                //列的options的key字符串为true或者1时，认为是true，默认为false
                                boolean key = false;
                                //布尔类型，只取前两个option的key来处理，多余的option不管，默认第一个和第二个option就是布尔类型的两个情况
                                if (i < 0x2) {
                                    if (columnDto.getOptions().get(i).getKey().equals("true") ||
                                            columnDto.getOptions().get(i).getKey().equals("1")) {
                                        key = true;
                                    }
                                    for (Map<String, Object> map : data) {
                                        String obj = map.get(columnDto.getCode()).toString();
                                        //数据值默认为false
                                        boolean colvalue = false;
                                        //数据值为字符串的true或者1时，数据值认为是true，默认为false
                                        if (obj.equals("true") || obj.equals("1")) {
                                            colvalue = true;
                                        }
                                        if (key == colvalue) {
                                            count++;
                                        }
                                    }
                                    if (count == 0x0) isZeroCount++;
                                    KeyValuePairDto colOption = new KeyValuePairDto();
                                    colOption.setKey(columnDto.getOptions().get(i).getValue());
                                    colOption.setValue(String.valueOf(count));
                                    colOptions.add(colOption);
                                }
                            }
                            statustsDto.setKey(columnDto.getName());
                            //statustsDto.setValue(colOptions);
                            //一个状态列中将状态统计结果为0的处理显示为"其它(n)"，n表示为0的状态数量，为1不合并显示
                            if (isZeroCount > 0x1) {
                                statustsDto.setValue(mergeOptions(colOptions, isZeroCount));
                            } else {
                                statustsDto.setValue(colOptions);
                            }
                            statusts.add(statustsDto);
                        }

                    }
                }
            }
        }
        long ratestsEndTime = System.currentTimeMillis();
        System.out.println("===状态统计结束时间====" + ratestsEndTime);
        System.out.println("======状态统计消耗时间===" + (ratestsEndTime - ratestsBeginTime) + "ms");
        return statusts;
    }

    /**
     * 状态统计合并处理：
     * 一个状态列中将状态统计结果为0的处理显示为"其它(n)"，n表示为0的状态数量，为1不合并显示
     *
     * @param sourceOptions 源数据 一个状态列的所有option
     * @param isZeroCount   状态统计结果为0的状态数量
     * @return List<KeyValuePairDto>
     * @author QuCheng Tech
     * @since 2024/5/10
     */
    List<KeyValuePairDto> mergeOptions(List<KeyValuePairDto> sourceOptions, int isZeroCount) {
        List<KeyValuePairDto> mergeOptions = new ArrayList<>();
        //找出状态数量不为0的
        for (KeyValuePairDto dto : sourceOptions) {
            if (Integer.parseInt(dto.getValue()) != 0x0) {
                //count不为0，不需要合并
                mergeOptions.add(dto);
            }
        }
        //状态数量为0的合并统计
        KeyValuePairDto mergeOption = new KeyValuePairDto();
        mergeOption.setKey("其它(" + isZeroCount + ")");
        mergeOption.setValue("0");
        mergeOptions.add(mergeOption);
        return mergeOptions;
    }

    @PostMapping(value = "/save")
    public String save(@RequestBody ExchangeDataSaveDto data) throws QCPromptException {
        //可能存在保存数据的属性需要从公用参数json中获取，因此需要传入或者查询出参数信息
        if (data == null)
            return "保存数据对象不能为空";
        if (data.getData() == null || data.getData().size() < 0x1)
            return "保存数据的数据值集合不能为空";
        //根据交换数据分类获取对应的数据库表编码
        ExchangeCategoryDto category = categoryService.get(data.getCid());
        if (category == null)
            return "根据指定的通道ID获取到的数据交换通道信息为空";
        if (StringUtils.isBlank(category.getTablecode()))
            return "根据指定的通道ID获取到的数据交换通道信息中数据库表为空，无法进行数据保存操作";

        DBTableDataOperateDto tableDataSaveDto = new DBTableDataOperateDto();
        tableDataSaveDto.setTablecode(category.getTablecode());
        tableDataSaveDto.setData(data.getData());
        tableDataSaveDto.setObjid(data.getObjid());
        tableDataSaveDto.setObjname(data.getObjname());
        tableDataSaveDto.setObjparam(data.getObjparam());

        //调用平台中的数据库表数据保存接口；如果需要用户信息，根据token获取
        return restTemplatePlatform.saveDbTableData(tableDataSaveDto, null);
    }

    /***
     * 从查询数据结果中获取列信息
     *
     * @param data 查询数据结果行
     * @return java.util.List<java.lang.String>
     * @author QuCheng Tech
     * @since 2024/1/30
     */
    LinkedHashMap<String, String> getQueryFieldNamesFromResultData(Map<String, Object> data) {
        if (data != null && data.size() > 0x0) {
            LinkedHashMap<String, String> result = new LinkedHashMap<>();

            for (String str : data.keySet()) {
                result.put(str, str);
            }

            return result;
        }

        return null;
    }

    /***
     * 从指定的sql语句中获取查询的字段集合(key为原始字段名称，value为别名)；如 stcd as code
     * stcd用于根据数据库表列信息获取，code用于最终对应数据行的显示
     *
     * @param sql sql语句
     * @return java.util.List<java.lang.String>
     * @author QuCheng Tech
     * @since 2024/1/30
     */
    LinkedHashMap<String, String> getQueryFieldNamesFromSql(String sql) {
        if (!StringUtils.isBlank(sql)) {
            //select xx1,xx2 as xx2,xxx as xx3 from ...
            //select CONVERT(varchar(100), INSERTTIME, 126) as INSERTTIME from
            //固定从select和from中间获取，中间的使用逗号分隔
            int fieldStringBeginIndex = sql.indexOf("select ");
            if (fieldStringBeginIndex >= 0x0) {
                int fieldStringEndIndex = sql.indexOf(" from ", fieldStringBeginIndex + 0x7);
                //System.out.println("getQueryFieldNamesFromSql fieldStringBeginIndex=" + fieldStringBeginIndex + " fieldStringEndIndex=" + fieldStringEndIndex);
                //System.out.println("getQueryFieldNamesFromSql fieldStringBeginIndex=" + sql.substring(fieldStringBeginIndex));
                //System.out.println("getQueryFieldNamesFromSql fieldStringEndIndex=" + sql.substring(fieldStringEndIndex));
                if (fieldStringEndIndex > fieldStringBeginIndex) {
                    String fieldString = sql.substring(fieldStringBeginIndex + 0x7, fieldStringEndIndex);
                    //System.out.println("getQueryFieldNamesFromSql fieldString=" + fieldString);
                    if (!StringUtils.isBlank(fieldString)) {
                        String[] fields = StringSplitUtil.Split(fieldString, ",", true);
                        if (fields != null && fields.length > 0x0) {
                            LinkedHashMap<String, String> result = new LinkedHashMap<>();

                            for (String str : fields) {
                                System.out.println("str=" + str);
                                //判断中间是否有as
                                //有一种使用格式转换的，如：CONVERT(varchar(100), INSERTTIME, 126) as INSERTTIME
                                int asIndex = str.indexOf(" as ");
                                if (asIndex > 0x0) {
                                    //String colName = str.substring(0x0, asIndex).trim().toLowerCase();
                                    //String aliName = str.substring(asIndex + 0x4).trim().toLowerCase();
                                    String colName = str.substring(0x0, asIndex).trim();
                                    String aliName = str.substring(asIndex + 0x4).trim();
                                    //有as时不去前面的，直接使用别名
                                    //result.put(colName, aliName);
                                    result.put(aliName, aliName);
                                } else {
                                    //没有as
                                    //result.put(str.toLowerCase(), null);
                                    result.put(str, null);
                                }
                            }

                            return result;
                        }
                    }
                }
            }
        }
        return null;
    }

    /***
     * 使用第三方库JSqlParser解析sql语句得到查询的列集合
     *
     * @param sql 查询sql语句
     * @return java.util.LinkedHashMap<java.lang.String, java.lang.String>
     * @author QuCheng Tech
     * @since 2025/1/14
     */
    LinkedHashMap<String, String> getQueryFieldNamesFromSqlByJSqlParser(String sql) {
        //方法来源参考：解析SQL获取查询的列名 https://blog.csdn.net/Min_Monk/article/details/93531288
        try {
            CCJSqlParserManager sqlParserManager = new CCJSqlParserManager();
            Statement statement = sqlParserManager.parse(new StringReader(sql));
            if (statement instanceof Select) {
                Select selectStatement = (Select) statement;
                PlainSelect selectBody = (PlainSelect) selectStatement.getSelectBody();
                List<SelectItem> selectItemList = selectBody.getSelectItems();
                if (selectItemList != null && selectItemList.size() > 0x0) {
                    LinkedHashMap<String, String> result = new LinkedHashMap<>();

                    for (SelectItem item : selectItemList) {
                        if (item instanceof SelectExpressionItem) {
                            SelectExpressionItem selectExpressionItem = (SelectExpressionItem) item;
                            Alias alias = selectExpressionItem.getAlias();
                            SimpleNode node = selectExpressionItem.getExpression().getASTNode();
                            Object value = node.jjtGetValue();
                            String columnName = "";

                            if (value instanceof Column) {
                                Column columnField = (Column) value;
                                columnName = columnField.getColumnName();
                            } else if (value instanceof Function) {
                                //如：CONVERT(varchar(100), INSERTTIME, 126) as INSERTTIME
                                //value为：CONVERT(varchar(100), INSERTTIME, 126)
                                //alias为： as INSERTTIME
                                //alias.getName()为：INSERTTIME
                                //如果为Function时，alias不为空时使用alias作为列名称
                                if (alias != null) {
                                    columnName = alias.getName();
                                } else {
                                    Function functionField = (Function) value;
                                    columnName = functionField.getName();
                                }
                            } else {
                                //增加对select 'aaa' from table的支持
                                //如果alias不为空使用alias，否则使用value
                                if (alias != null) {
                                    columnName = alias.getName();
                                } else {
                                    //columnName= ObjectUtils.praseObjectToString(value);
                                    columnName = value.toString();
                                    columnName = columnName.replace("'", "");
                                    columnName = columnName.replace("\"", "");
                                }
                            }

                            System.out.println("getQueryFieldNamesFromSqlByJSqlParser foreach SelectExpressionItem，instanceof=" + item.getClass() + ",value=" + value + ",alias=" + alias + ",columnName=" + columnName);
                            result.put(columnName, alias == null ? columnName : alias.getName());
                        } else if (item instanceof AllTableColumns) {
                            System.out.println("getQueryFieldNamesFromSqlByJSqlParser for，item is AllTableColumns，" + item.toString());
                        } else {
                            System.out.println("getQueryFieldNamesFromSqlByJSqlParser for，item is Unknown，" + item.toString());
                        }
                    }

                    return result;
                }
            }
        } catch (JSQLParserException ex) {
            ex.printStackTrace();
        }

        return null;
    }
}
